/**介绍
函数是JavaScript应用程序的基础。 它帮助你实现抽象层，模拟类，信息隐藏和模块。 在TypeScript里，虽然已经支持类，命名空间和模块，但函数仍然是主要的定义 行为的地方。 TypeScript为JavaScript函数添加了额外的功能，让我们可以更容易地使用。

函数
和JavaScript一样，TypeScript函数可以创建有名字的函数和匿名函数。 你可以随意选择适合应用程序的方式，不论是定义一系列API函数还是只使用一次的函数。

通过下面的例子可以迅速回想起这两种JavaScript中的函数： */
function add(x,y){
  return x+y;
}
//匿名函数
let myAdd = function(x,y){return x+y};


function tsAdd(x:number,y:number){
  return x+y;
}
let myTsAdd = function(x:number,y:number):number{return x+y};
let myTsAdd2 = function(x:number,y:number){return x+y};  //推断类型返回number, :number可以不写

//函数的完整类型
let myAdd3: (x:number,y:number) => number = function(x:number,y:number):number{return x+y;}
//推断类型
let myAdd4: (baseValue: number, increment: number) => number =
    function(x, y) { return x + y; };

//剩余参数 ...,在TypeScripts里面可以把所有的参数收集到一个变量里面
function buildName(firstName:string, ...restOfName:string[]){
  return firstName+" "+restOfName.join(" ");
}
let empName = buildName("Join","bulen","marks","nuse");











/**this和箭头函数
JavaScript里，this的值在函数被调用的时候才会指定。 这是个既强大又灵活的特点，但是你需要花点时间弄清楚函数调用的上下文是什么。 但众所周知，这不是一件很简单的事，尤其是在返回一个函数或将函数当做参数传递的时候。 */
let deck = {
  suits: ["hearts", "spades", "clubs", "diamonds"],
  cards: Array(52),

  //不同于Java，typescript可以返回一个函数
  createCardPicker: function() {
      // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here
      return () => {
          let pickedCard = Math.floor(Math.random() * 52);
          let pickedSuit = Math.floor(pickedCard / 13);

          return {suit: this.suits[pickedSuit], card: pickedCard % 13};
      }
  }
}
//获取返回函数
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker(); //调用函数获取返回对象 {}

alert("card: " + pickedCard.card + " of " + pickedCard.suit);


function f(this: void) {
  // make sure `this` is unusable in this standalone function
}

interface Card{
  suit: string;
  card: number;
}
interface Deck{
  suits: string[];
  cards: number[];
  createCardPicker(this: Deck):() => Card;
}
let deck2:Deck = {
  suits:["header","spades","clubs","diamonds"],
  cards: Array(52),
  createCardPicker: function(this: Deck){
    return () =>{
      let pickedCard = Math.floor(Math.random() * 52);
      let pickedSuit = Math.floor(pickedCard / 13);

      return {suit: this.suits[pickedSuit], card: pickedCard % 13};
    }
  }
}
let card = deck2.createCardPicker();
let p = card();
alert("card: " + p.card + " of " + p.suit);


/**
 * 重载
JavaScript本身是个动态语言
pickCard方法根据传入参数的不同会返回两种不同的类型。 如果传入的是代表纸牌的对象，函数作用是从中抓一张牌。 如果用户想抓牌，我们告诉他抓到了什么牌。 但是这怎么在类型系统里表示呢。
方法是为同一个函数提供多个函数类型定义来进行函数重载。 编译器会根据这个列表去处理函数的调用。 下面我们来重载 pickCard函数。
 * 
*/
let suits = ["hearts", "spades", "clubs", "diamonds"];
function pickCard(x: {suit: string; card: number; }[]): number;
function pickCard(x: number): {suit: string; card: number; };

//注意，function pickCard(x): any并不是重载列表的一部分，因此这里只有两个重载：一个是接收对象另一个接收数字。 以其它参数调用 pickCard会产生错误。
function pickCard(x): any {
    // Check to see if we're working with an object/array
    // if so, they gave us the deck and we'll pick the card
    if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
    }
    // Otherwise just let them pick the card
    else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
    }
}